package com.snake;

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.net.URL;
import java.util.Random;

import javax.swing.Timer;

// 游戏
@SuppressWarnings({ "deprecation", "removal" })
class Game implements ActionListener {
    // 回调
    public interface Callback {
        void onDifficultyUp(int difficulty);    // 难度增加事件
        void onGameOver(boolean isWon);         // 游戏结束事件
        void onScore(int score);                // 得分事件
    }

    // 状态
    public enum Status {
        END,    // 已结束
        PAUSED, // 暂停中
        PLAYING // 游戏中
    }

    private SnakeBody mApple;               // 果子
    private final Callback mCallback;       // 回调
    private int mDifficulty;                // 难度
    private final Graphics mDrawer;         // 绘图者
    private boolean mEnableMusic;           // 音乐开关
    private final Component mGameBoard;     // 游戏区域
    private final AudioClip mMusicPlayer;   // 音乐播放者
    private final Random mRandom;           // 随机数生成器
    private int mScore;                     // 得分
    private Snake mSnake;                   // 蛇
    private Status mStatus;                 // 状态
    private final Timer mTimer;             // 定时器

    /**
     * 构造方法。
     * @param callback  回调
     * @param drawer    绘图者
     * @param gameBoard 游戏区域
     */
    public Game(Callback callback, Graphics drawer, Component gameBoard) {
        // 初始化成员
        mCallback = callback;
        mDifficulty = 1;
        mDrawer = drawer;
        mEnableMusic = false;
        mGameBoard = gameBoard;
        URL url = null;
        try {
            url = new File(Const.MUSIC_FILE_NAME).toURL();
        } catch (Exception e) {
            e.printStackTrace();
        }
        mMusicPlayer = Applet.newAudioClip(url);
        mRandom = new Random();
        mScore = 0;
        mStatus = Status.END;
        mTimer = new Timer(0, this);

        // 重绘游戏区域
        this.drawGameBoard();
        mGameBoard.repaint();
    }

    /**
     * 定时器事件的响应方法。
     * @param e 事件相关信息
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        // 移动蛇
        final boolean isDead = mSnake.move();
        if (isDead) {
            this.gameOver(false);
            return;
        }

        // 检查是否碰到果子
        final SnakeBody head = mSnake.getHead();
        if (head.getPosition().equals(mApple.getPosition())) {
            // 如果碰到果子，将果子加入蛇链表
            mSnake.eat(mApple);
            this.createApple();
            mScore++;
            mCallback.onScore(mScore);

            // 判断是否胜利
            if (this.isWon()) {
                this.gameOver(true);
                return;
            }

            // 判断是否达到增加难度的条件
            if (mScore % Const.DIFF_BASE == 0 && mDifficulty != Const.DIFF_MAX) {
                mDifficulty++;
                mCallback.onDifficultyUp(mDifficulty);
                mTimer.stop();
                mTimer.setDelay(Const.TIMER_INIT - Const.TIMER_GROW * (mDifficulty - 1));
                mTimer.start();
            }
        }
        
        // 通知游戏区域重绘
        mGameBoard.repaint();
    }

    /**
     * 在随机位置创建果子。
     */
    private void createApple() {
        final Point point = new Point();
        boolean used = false;

        // 创建果子
        do {
            // 取 x、y 的随机值
            point.x = mRandom.nextInt(Const.GAME_BOARD_GRID_COUNT_H - 2) + 1;
            point.y = mRandom.nextInt(Const.GAME_BOARD_GRID_COUNT_V - 2) + 1;
            used = this.pointIsUsed(point);
        } while (used);
        mApple = new SnakeBody(mDrawer, point, SnakeBody.Type.APPLE);
    }

    /**
     * 画游戏区域。
     */
    private void drawGameBoard() {
        mDrawer.setColor(Const.GAME_BOARD_COLOR);
        mDrawer.fillRect(0, 0, Const.GAME_BOARD_WIDTH, Const.GAME_BOARD_HEIGHT);
    }

    /**
     * 游戏结束。
     * @param isWon 胜利则为 true，否则为 false
     */
    private void gameOver(boolean isWon) {
        // 将状态设置为已结束
        mStatus = Status.END;

        // 停止定时器
        mTimer.stop();

        // 停止播放音乐
        if (mEnableMusic) {
            mMusicPlayer.stop();
        }

        // 发送通知
        mCallback.onGameOver(isWon);
    }

    /**
     * 获取游戏状态。
     * @return 游戏状态
     */
    public Status getStatus() {
        return mStatus;
    }

    /**
     * 判断是否胜利。
     * @return 胜利则返回 true，否则返回 false
     */
    private boolean isWon() {
        return mScore >= Const.WIN_NUMBER;
    }

    /**
     * 按键事件的响应方法。
     * @param key   按键
     */
    public void onKeyPressed(int key) {
        if (mStatus != Status.PLAYING) {
            return;
        }
        if (key >= KeyEvent.VK_LEFT && key <= KeyEvent.VK_DOWN) {
            mSnake.turn(key);
        }
    }

    /**
     * 游戏暂停。
     */
    public void pause() {
        if (mStatus == Status.PLAYING) {
            // 停止定时器
            mTimer.stop();
            
            // 停止播放音乐
            if (mEnableMusic) {
                mMusicPlayer.stop();
            }

            // 将状态设置为已暂停
            mStatus = Status.PAUSED;
        } else if (mStatus == Status.PAUSED) {
            // 开启定时器
            mTimer.setDelay(Const.TIMER_INIT - Const.TIMER_GROW * (mDifficulty - 1));
            mTimer.start();

            // 播放音乐
            if (mEnableMusic) {
                mMusicPlayer.loop();
            }            

            // 将状态设置为游戏中
            mStatus = Status.PLAYING;
        }
    }

    /**
     * 播放音乐。
     * @return 正在播放音乐则为 true，否则为 false
     */
    public boolean playMusic() {
        mEnableMusic = !mEnableMusic;

        if (mEnableMusic) {
            if (mStatus == Status.PLAYING) {
                mMusicPlayer.loop();
            }
        } else {
            mMusicPlayer.stop();
        }

        return mEnableMusic;
    }

    /**
     * 判断某个点是否被占用。
     * @param point 点
     * @return 被占用了则返回 true，否则返回 false
     */
    private boolean pointIsUsed(Point point) {
        // 检查点是否在蛇上
        if (mSnake != null) {
            if (mSnake.pointIsOnMe(point)) {
                return true;
            }
        }

        // 检查点是否在果子上
        if (mApple != null) {
            return mApple.getPosition().equals(point);
        }

        return false;
    }

    /**
     * 游戏开始。
     */
    public void start() {
        // 重绘游戏区域
        this.drawGameBoard();

        // 创建蛇
        mSnake = new Snake(mDrawer);

        // 创建果子
        this.createApple();

        // 通知游戏区域重绘
        mGameBoard.repaint();

        // 重置难度
        mDifficulty = 1;

        // 重置分数
        mScore = 0;

        // 开启定时器
        mTimer.stop();
        mTimer.setDelay(Const.TIMER_INIT);
        mTimer.start();
        
        // 播放音乐
        if (mEnableMusic) {
            mMusicPlayer.loop();
        }

        // 将状态设置为游戏中
        mStatus = Status.PLAYING;
    }
}
